package Dispatcher;

import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Observable;
import java.util.concurrent.*;

/**
 * Group Dispatcher receives the floor calls and elevator destinations and
 * spawns various threads to send informations to different elevators
 * 
 * @author Varun Maryada, Sanat Dixit, Steve Tannahill
 * 
 */
public class GroupDispatcher extends Observable{
	//maximum number of elevators
	public static final int NUMBER_OF_ELEVATORS = 10;
	
	// receive these kind of messages
	public static final int RECEIVED_INIT = 1;
	public static final int RECEIVED_UPDATE = 4;
	public static final int REGISTRATION_COMPLETE = 2;
	public ArrayList<CommunicationsThread> commThreads;
	public ArrayList<Integer> registeredElevators;
	
	public ServerSocket serverSocket;
	public int openSockets =0;
	public ConcurrentHashMap<Integer, PrintWriter> elevators;
	public ConcurrentHashMap<Integer, ElevatorStatus> status;
	
	public GroupDispatcher() {
		this.elevators = new ConcurrentHashMap<Integer, PrintWriter>();
		this.status = new ConcurrentHashMap<Integer, ElevatorStatus>();
		this.commThreads = new ArrayList<CommunicationsThread>();
		this.registeredElevators = new ArrayList<Integer>();
		try {
			this.serverSocket = new ServerSocket(5000);
		} catch (IOException e) {
			System.out.println("Could not open server socket at port 5000");
		}
		this.createConnections();
	}

	public void createConnections() {
		for (int i = 0; i < NUMBER_OF_ELEVATORS; i++) {
			this.commThreads.add(new CommunicationsThread(this));
			this.commThreads.get(i).start();
			
		}
	}
	
	public ConcurrentHashMap<Integer, PrintWriter> getElevators() {
		return elevators;
	}

	public ConcurrentHashMap<Integer, ElevatorStatus> getStatus() {
		return status;
	}

	public synchronized void handle(String msg, PrintWriter out) {
		
		String[] sMsg = msg.split("/");
		if(sMsg.length == 0){
			return;
		}
		int msgType = Integer.parseInt(sMsg[0]);
		if (msgType == RECEIVED_INIT) {
			this.handleInit(sMsg, out);
		} else if (msgType == RECEIVED_UPDATE) {
			this.handleUpdate(sMsg);
		} else {
			
			System.out.println("Received unknown message from elevator");
		}
	}

	public synchronized void handleInit(String[] sMsg, PrintWriter out) {
		
		if(sMsg.length < 2){
			return;
		}
		int id = Integer.parseInt(sMsg[1]);
		if (id <= 0) {
			return;
		}
		if (this.registeredElevators.contains(id)) {
			return;
		}
		System.out.println("Found Elevator: " + id);
		this.elevators.put(id, out);
		this.registeredElevators.add(id);
		// send a start message to elevator
		elevators.get(id).println(REGISTRATION_COMPLETE + "\n");
		
		//create a hash map with key id and value print writer
		//in presence of GUI, send it the elevator id
		HashMap<Integer, PrintWriter> map = new HashMap<Integer, PrintWriter>();
		map.put(id, elevators.get(id));
		this.notifyGUI(map);
	}
	
	public void notifyGUI(Object o) {
		if (this.countObservers() > 0) {
			this.setChanged();
			this.notifyObservers(o);
		}
	}

	public synchronized void handleUpdate(String[] sMsg) {
		if(sMsg.length < 6){
			return;
		}
		int id = Integer.parseInt(sMsg[1]);
		int state = Integer.parseInt(sMsg[2]);
		int currentFloor = Integer.parseInt(sMsg[3]);
		int nextDest = Integer.parseInt(sMsg[4]);
		int direction = Integer.parseInt(sMsg[5]);
		int destCount = Integer.parseInt(sMsg[6]);
		
		this.status.put(id, new ElevatorStatus(id,
				state, currentFloor, nextDest, direction, destCount));
//		System.out.println(status.toString());
		this.notifyGUI(this.status);
	}

	public synchronized void processHallCall(int floorNumber, int direction) {
		SendHallCallRequestThread srt = new SendHallCallRequestThread(
				floorNumber, direction, this);
		srt.start();
	}

	public void sendMessage(int elevatorNum, String Msg) {

	}

	@Override
	public void finalize() {
		try {
			// close all sockets and PrintWriters
			this.serverSocket.close();
		} catch (IOException e) {
			System.out.println("IO Exception");
		}
	}

	public void cleanUp(int index) {
		try {
			// close all sockets and PrintWriters
			this.serverSocket.close();
		} catch (IOException e) {
			System.out.println("IO Exception");
		}
	}
	
	public static void main(String args[]) {
		System.out.println("Started dispatcher");
		GroupDispatcher gd = new GroupDispatcher();
		// create and start an update thread if needed
		UpdateThread ut = new UpdateThread(gd);
		ut.start();
		try {
			ut.join();
		} catch (InterruptedException e) {
			System.out.println("Update Thread Interrupted");
		}
		
		gd.finalize();
	}
}
